package com.platform.common.utils;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.Modifier;
import javassist.NotFoundException;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;

/**
 * <pre>
 * 功能：类工具
 * </pre>
 * 
 * @author wei.gao
 */
public class ClassUtils extends org.apache.commons.lang3.ClassUtils {
	
	/**
	 * <pre>
	 * 功能：从类集合中挑出接口的实现类
	 * </pre>
	 * @param clazz
	 * @param classesAll
	 * @return
	 */
	public static List<Class<?>> getByInterface(List<Class<?>> classesAll, Class<?> clazz) {
		List<Class<?>> classes = new ArrayList<Class<?>>();
		//获取指定接口的实现类
		if (clazz.isInterface()) {
			Iterator<Class<?>> iterator = classesAll.iterator();
			while (iterator.hasNext()) {
				Class<?> cls = iterator.next();
				if (clazz.isAssignableFrom(cls)) {
					if (!clazz.equals(cls)) {//自身并不加进去
						classes.add(cls);
					}
				}
			}
		}
		return classes;
	}
	
	/**
	 * 从包package中获取所有的Class
	 *
	 * @param pack
	 * @return
	 * @throws IOException 
	 * @throws ClassNotFoundException 
	 */
	public static List<Class<?>> getAllClasses(String pack) throws IOException, ClassNotFoundException {
		Set<Class<?>> classes = new LinkedHashSet<Class<?>>();
		// 是否循环迭代
		String packageName = pack;
		String packageDirName = packageName.replace('.', '/');
		Enumeration<URL> dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
		while (dirs.hasMoreElements()) {
			URL url = dirs.nextElement();
			String protocol = url.getProtocol();
			if ("file".equals(protocol)) {
				String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
				findAndAddClassesInPackageByFile(packageName, filePath, classes);
			} else if ("jar".equals(protocol)) {
				findAndAddClassesInPackageByJar(url, packageName, packageDirName, classes);
			}
		}
		return new ArrayList<Class<?>>(classes);
	}
	
	/**
	 * <pre>
	 * 功能：以Jar的形式来获取包下的所有Class
	 * </pre>
	 * 
	 * @param packageName
	 * @param packagePath
	 * @param recursive
	 * @param classes
	 * @throws ClassNotFoundException 
	 * @throws IOException 
	 */
	private static void findAndAddClassesInPackageByJar(URL url, String packageName, String packageDirName, Set<Class<?>> classes) throws ClassNotFoundException, IOException {
		JarFile jar = ((JarURLConnection) url.openConnection()).getJarFile();
		Enumeration<JarEntry> entries = jar.entries();
		while (entries.hasMoreElements()) {
			JarEntry entry = entries.nextElement();
			String name = entry.getName();
			// 如果是以/开头的
			if (name.charAt(0) == '/') {
				// 获取后面的字符串
				name = name.substring(1);
			}
			// 如果前半部分和定义的包名相同
			if (name.startsWith(packageDirName)) {
				int idx = name.lastIndexOf('/');
				// 如果以"/"结尾 是一个包
				if (idx != -1) {
					// 获取包名 把"/"替换成"."
					packageName = name.substring(0, idx).replace('/', '.');
				}
				// 如果可以迭代下去 并且是一个包
				if ((idx != -1)) {
					// 如果是一个.class文件 而且不是目录
					if (name.endsWith(".class") && !entry.isDirectory()) {
						// 去掉后面的".class" 获取真正的类名
						String className = name.substring(packageName.length() + 1, name.length() - 6);
						try {
							// 如果是接口的实现则添加到集合中去
							Class<?> clazz = Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className);
							classes.add(clazz);
						} catch (NoClassDefFoundError e) {
							// 该异常为项目中某些开源包的依赖包没引入导致，不影响搜索结果，不予打印
						}
					}
				}
			}
		}
	}
	
	/**
	 * <pre>
	 * 功能：以文件的形式来获取包下的所有Class
	 * </pre>
	 * 
	 * @param packageName
	 * @param packagePath
	 * @param recursive
	 * @param classes
	 * @throws ClassNotFoundException 
	 */
	private static void findAndAddClassesInPackageByFile(String packageName, String packagePath, Set<Class<?>> classes) throws ClassNotFoundException {
		File dir = new File(packagePath);
		if (!dir.exists() || !dir.isDirectory()) {
			return;
		}
		// 如果存在 就获取包下的所有文件 包括目录
		File[] dirfiles = dir.listFiles(new FileFilter() {
			// 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
			public boolean accept(File file) {
				return (file.isDirectory()) || (file.getName().endsWith(".class"));
			}
		});
		// 循环所有文件
		for (File file : dirfiles) {
			// 如果是目录 则继续扫描
			if (file.isDirectory()) {
				findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), classes);
			} else {
				// 如果是java类文件去掉后面的.class 只留下类名
				String className = file.getName().substring(0, file.getName().length() - 6);
				try {
					// 如果是接口的实现则添加到集合中去
					Class<?> clazz = Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className);
					classes.add(clazz);
				} catch (NoClassDefFoundError e) {
					// 该异常为项目中某些开源包的依赖包没引入导致，不影响搜索结果，不予打印
				}
			}
		}
	}
	
	/**
	 * <pre>
	 * 功能：获取方法参数名称，按给定的参数类型匹配方法
	 * </pre>
	 * 
	 * @param clazz
	 * @param method
	 * @param paramTypes
	 * @return
	 * @throws NotFoundException
	 * @throws MissingLVException
	 */
	public static String[] getMethodParamNames(Class<?> clazz, String method, Class<?>... paramTypes) throws NotFoundException, MissingLVException {
		ClassPool pool = ClassPool.getDefault();
		CtClass cc = pool.get(clazz.getName());
		String[] paramTypeNames = new String[paramTypes.length];
		for (int i = 0; i < paramTypes.length; i++) {
			paramTypeNames[i] = paramTypes[i].getName();
		}
		CtMethod cm = cc.getDeclaredMethod(method, pool.get(paramTypeNames));
		return getMethodParamNames(cm);
	}
	
	/**
	 * <pre>
	 * 功能：获取方法参数名称，匹配同名的某一个方法
	 * </pre>
	 * 
	 * @param clazz
	 * @param method
	 * @return
	 * @throws NotFoundException
	 * @throws MissingLVException
	 */
	public static String[] getMethodParamNames(Class<?> clazz, String method) throws NotFoundException, MissingLVException {
		ClassPool pool = ClassPool.getDefault();
		ClassClassPath classPath = new ClassClassPath(clazz);
		pool.insertClassPath(classPath);
		CtClass cc = pool.get(clazz.getName());
		CtMethod cm = cc.getDeclaredMethod(method);
		return getMethodParamNames(cm);
	}
	
	/**
	 * <pre>
	 * 功能：获取方法参数名称
	 * </pre>
	 * 
	 * @param cm
	 * @return
	 * @throws NotFoundException
	 * @throws MissingLVException
	 */
	protected static String[] getMethodParamNames(CtMethod cm) throws NotFoundException, MissingLVException {
		CtClass cc = cm.getDeclaringClass();
		MethodInfo methodInfo = cm.getMethodInfo();
		CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
		LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
		if (attr == null) {
			throw new MissingLVException(cc.getName());
		}
		String[] paramNames = new String[cm.getParameterTypes().length];
		int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
		for (int i = 0; i < paramNames.length; i++) {
			paramNames[i] = attr.variableName(i + pos);
		}
		return paramNames;
	}
	
	/**
	 * <pre>
	 * 功能：
	 * </pre>
	 * 
	 * @param clazz
	 * @param method
	 * @return
	 * @throws NotFoundException
	 * @throws MissingLVException
	 */
	public static CtClass[] getMethodParamTypes(Class<?> clazz, String method) throws NotFoundException, MissingLVException {
		ClassPool pool = ClassPool.getDefault();
		ClassClassPath classPath = new ClassClassPath(clazz);
		pool.insertClassPath(classPath);
		CtClass cc = pool.get(clazz.getName());
		CtMethod cm = cc.getDeclaredMethod(method);
		return cm.getParameterTypes();
	}
	
	/**
	 * <pre>
	 * 功能：在class中未找到局部变量表信息<br>
	 * 使用编译器选项 javac -g:{vars}来编译源文件
	 * </pre>
	 * 
	 * @author wei.gao
	 */
	public static class MissingLVException extends Exception {
		private static final long serialVersionUID = 1L;
		static String msg = "class:%s 不包含局部变量表信息，请使用编译器选项 javac -g:{vars}来编译源文件。";
		
		public MissingLVException(String clazzName) {
			super(String.format(msg, clazzName));
		}
	}
	
	/**
	 * <pre>
	 * 功能：动态调用方法
	 * </pre>
	 * 
	 * @param target
	 * @param method
	 * @param args
	 * @return
	 * @throws SecurityException
	 * @throws NoSuchMethodException
	 * @throws InvocationTargetException
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 */
	public static Object getInvokeReturn(Object target, String method, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
		if (target == null) {
			return null;
		}
		args = ValidateUtils.isNullArray(args) ? new Object[] {} : args;
		Class<?>[] clazz = new Class[args.length];
		for (int i = 0, j = args.length; i < j; i++) {
			clazz[i] = args[i].getClass();
		}
		return target.getClass().getMethod(method, clazz).invoke(target, ValidateUtils.isNullArray(args) ? new Object[0] : args);
	}
	
	/**
	 * <pre>
	 * 功能：动态调用类静态方法
	 * </pre>
	 * 
	 * @param targetClazz
	 * @param method
	 * @param args
	 * @return
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 * @throws InvocationTargetException
	 * @throws NoSuchMethodException
	 * @throws SecurityException
	 */
	public static Object getInvokeStaticReturn(Class<?> targetClazz, String method, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
		args = ValidateUtils.isNullArray(args) ? new Object[] {} : args;
		Class<?>[] clazz = new Class[args.length];
		for (int i = 0, j = args.length; i < j; i++) {
			clazz[i] = args[i].getClass();
		}
		return targetClazz.getMethod(method, clazz).invoke(targetClazz, ValidateUtils.isNullArray(args) ? new Object[0] : args);
	}
	
	/**
	 * <pre>
	 * 功能：获得泛型的对象,可以设置默认值
	 * </pre>
	 * 
	 * @param clazz
	 * @param value
	 * @param t
	 * @return
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 * @throws InvocationTargetException
	 * @throws NoSuchMethodException
	 * @throws SecurityException
	 */
	@SuppressWarnings("unchecked")
	public static <T> T getEnumType(Class<T> clazz, String value, T t) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
		return (T) getInvokeStaticReturn(clazz, "valueOf", value);
	}
}
